<?php
/**
 * Created by Benjamin.
 * Date: 2020/4/30
 * Time: 10:18
 */
namespace App\WebSocket;


use App\Services\AppService;
use App\Services\HelpService;
use EasySwoole\Component\TableManager;
use EasySwoole\EasySwoole\Config;
use EasySwoole\Socket\AbstractInterface\ParserInterface;
use EasySwoole\Socket\Client\WebSocket;
use EasySwoole\Socket\Bean\Caller;
use EasySwoole\Socket\Bean\Response;
use EasySwoole\EasySwoole\ServerManager;

/**
 * Class WebSocketParser
 *
 * 此类是自定义的 websocket 消息解析器
 * 此处使用的设计是使用 json string 作为消息格式
 * 当客户端消息到达服务端时，会调用 decode 方法进行消息解析
 * 会将 websocket 消息 转成具体的 Class -> Action 调用 并且将参数注入
 *
 * @package App\WebSocket
 */
class WebSocketParser implements ParserInterface
{
    /**
     * decode
     * @param  string         $raw    客户端原始消息
     * @param  WebSocket      $client WebSocket Client 对象
     * @return Caller         Socket  调用对象
     */
    public function decode($raw, $client) : ? Caller
    {
        $server = ServerManager::getInstance()->getSwooleServer();
        $fdToAppTable = TableManager::getInstance()->get(Config::getInstance()->getConf('CHAT.fd_to_app'));
        $fdToApp =  $fdToAppTable->get($client->getFd());

        $jsonData = HelpService::aesDecrypt($raw,$fdToApp['access_key'],$fdToApp['secret_key']);
       # echo '收到消息'.PHP_EOL;
      #  echo    $jsonData.PHP_EOL ;
        // 解析 客户端原始消息
        $data = json_decode(preg_replace('/[\x00-\x1F]/','', $jsonData),true);
        if(empty($data)){
            $data = json_decode($raw,true);
        }
        $clientInfo =  $server->getClientInfo($client->getFd());
        $table = TableManager::getInstance()->get(Config::getInstance()->getConf('CHAT.online_user'));
        if(isset($clientInfo['uid'])){
            $data['userInfo'] =  $table->get($clientInfo['uid']);
            $data['userInfo']['id'] = $clientInfo['uid'];
        }
        $data['fd'] = $client->getFd();
       # echo '收到消息'.$raw.PHP_EOL;

     #   echo '消息结束'.PHP_EOL;

        // new 调用者对象
        $caller =  new Caller();
        /**
         * 设置被调用的类 这里会将ws消息中的 class 参数解析为具体想访问的控制器
         * 如果更喜欢 event 方式 可以自定义 event 和具体的类的 map 即可
         * 注 目前 easyswoole 3.0.4 版本及以下 不支持直接传递 class string 可以通过这种方式
         */
        $class = '\\App\\WebSocket\\'. ucfirst($data['class'] ?? 'Index');
  
        $caller->setControllerClass($class);

        // 设置被调用的方法
        $caller->setAction($data['action'] ?? 'index');
        #没有基础信息，直接干掉
        if($data['action']!='login'){
            $fdToAppTable = TableManager::getInstance()->get(Config::getInstance()->getConf('CHAT.fd_to_app'));
            $fdToApp =  $fdToAppTable->get($client->getFd());

            //全部在线用户集合
            #这边后面要加判断，是否已经在线，在线发个退出通知
            if(isset($data['userInfo'])){
                $onlineData = $table->get($data['userInfo']['id']);
                if(empty($fdToApp)||$onlineData['fd']!= $client->getFd()){//主动断开
                    $caller->setAction('autoOffline');
                }
            }else{

                echo $client->getFd().'找不到登录信息'.PHP_EOL;
                $caller->setAction('autoOffline');
            }
        }else{
            echo $client->getFd().'当前登录的fd'.PHP_EOL;
        }
        $args =   $data;
        $caller->setArgs($args ?? []);
        return $caller;
    }
    /**
     * encode
     * @param  Response     $response Socket Response 对象
     * @param  WebSocket    $client   WebSocket Client 对象
     * @return string             发送给客户端的消息
     */
    public function encode(Response $response, $client) : ? string
    {
        /**
         * 这里返回响应给客户端的信息
         * 这里应当只做统一的encode操作 具体的状态等应当由 Controller处理
         */
        return $response->getMessage();
    }
}